"""
模型API错误处理模块

此模块提供了针对模型API调用的详细错误处理机制，包括自定义异常类和错误处理装饰器。
"""

import functools
import time
import logging
from typing import Callable, Any, Optional, Dict, Type, Union
import requests


logger = logging.getLogger(__name__)


class ModelAPIError(Exception):
    """模型API基础异常类"""
    def __init__(self, message: str, status_code: Optional[int] = None, response_data: Optional[Dict] = None):
        super().__init__(message)
        self.status_code = status_code
        self.response_data = response_data


class ConnectionError(ModelAPIError):
    """网络连接错误"""
    pass


class TimeoutError(ModelAPIError):
    """请求超时错误"""
    pass


class ServerError(ModelAPIError):
    """服务器错误 (5xx)"""
    pass


class ClientError(ModelAPIError):
    """客户端错误 (4xx)"""
    pass


class InvalidResponseError(ModelAPIError):
    """无效响应错误"""
    pass


class RateLimitError(ModelAPIError):
    """请求频率限制错误"""
    pass


def handle_api_errors(func: Callable) -> Callable:
    """
    API错误处理装饰器
    
    Args:
        func: 需要装饰的函数
        
    Returns:
        装饰后的函数
    """
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        try:
            return func(*args, **kwargs)
        except requests.exceptions.ConnectionError as e:
            logger.error(f"网络连接错误: {str(e)}")
            raise ConnectionError(f"无法连接到模型服务器: {str(e)}")
        except requests.exceptions.Timeout as e:
            logger.error(f"请求超时: {str(e)}")
            raise TimeoutError(f"请求超时，服务器未及时响应: {str(e)}")
        except requests.exceptions.HTTPError as e:
            status_code = e.response.status_code if hasattr(e, 'response') and e.response else None
            response_text = e.response.text if hasattr(e, 'response') and e.response else str(e)
            
            logger.error(f"HTTP错误 - 状态码: {status_code}, 响应: {response_text}")
            
            if status_code and 400 <= status_code < 500:
                if status_code == 429:
                    raise RateLimitError(f"请求频率过高，请稍后再试: {response_text}", status_code)
                else:
                    raise ClientError(f"客户端请求错误: {response_text}", status_code)
            elif status_code and 500 <= status_code < 600:
                raise ServerError(f"服务器内部错误: {response_text}", status_code)
            else:
                raise ModelAPIError(f"HTTP错误: {response_text}", status_code)
        except requests.exceptions.RequestException as e:
            logger.error(f"请求异常: {str(e)}")
            raise ModelAPIError(f"请求过程中发生异常: {str(e)}")
        except ValueError as e:
            logger.error(f"响应解析错误: {str(e)}")
            raise InvalidResponseError(f"服务器响应格式无效: {str(e)}")
        except Exception as e:
            logger.error(f"未知错误: {str(e)}")
            raise ModelAPIError(f"未知错误: {str(e)}")
    
    return wrapper


def retry_on_error(max_retries: int = 3, 
                  retry_delay: float = 1.0, 
                  backoff_factor: float = 2.0,
                  retry_on: Optional[Union[Type[Exception], tuple]] = None) -> Callable:
    """
    重试装饰器，在遇到特定错误时自动重试
    
    Args:
        max_retries: 最大重试次数
        retry_delay: 初始重试延迟(秒)
        backoff_factor: 延迟递增因子
        retry_on: 需要重试的异常类型，默认为ConnectionError和TimeoutError
        
    Returns:
        装饰器函数
    """
    if retry_on is None:
        retry_on = (ConnectionError, TimeoutError, ServerError)
    
    def decorator(func: Callable) -> Callable:
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            last_exception = None
            current_delay = retry_delay
            
            for attempt in range(max_retries + 1):  # +1 包括初始尝试
                try:
                    return func(*args, **kwargs)
                except Exception as e:
                    last_exception = e
                    
                    # 检查是否是需要重试的错误类型
                    if not isinstance(e, retry_on):
                        raise
                    
                    # 如果是最后一次尝试，直接抛出异常
                    if attempt == max_retries:
                        logger.error(f"达到最大重试次数 {max_retries}，放弃重试")
                        raise
                    
                    logger.warning(f"请求失败 (尝试 {attempt + 1}/{max_retries + 1}): {str(e)}")
                    logger.info(f"等待 {current_delay:.1f} 秒后重试...")
                    
                    time.sleep(current_delay)
                    current_delay *= backoff_factor
            
            # 理论上不会执行到这里，但为了安全起见
            if last_exception:
                raise last_exception
                
        return wrapper
    return decorator


def log_api_call(func: Callable) -> Callable:
    """
    API调用日志装饰器，记录请求和响应信息
    
    Args:
        func: 需要装饰的函数
        
    Returns:
        装饰后的函数
    """
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        start_time = time.time()
        
        # 记录请求信息
        logger.info(f"调用API方法: {func.__name__}")
        logger.debug(f"请求参数: args={args}, kwargs={kwargs}")
        
        try:
            result = func(*args, **kwargs)
            
            # 计算响应时间
            elapsed_time = time.time() - start_time
            logger.info(f"API调用成功，耗时: {elapsed_time:.2f}秒")
            
            # 记录响应信息（简化版）
            if isinstance(result, dict):
                if "choices" in result and len(result["choices"]) > 0:
                    content = result["choices"][0].get("message", {}).get("content", "")
                    # 截断长内容
                    if len(content) > 100:
                        content = content[:100] + "..."
                    logger.debug(f"响应内容: {content}")
                elif "error" in result:
                    logger.warning(f"API返回错误: {result['error']}")
            
            return result
            
        except Exception as e:
            elapsed_time = time.time() - start_time
            logger.error(f"API调用失败，耗时: {elapsed_time:.2f}秒，错误: {str(e)}")
            raise
            
    return wrapper


# 错误码到异常类的映射
ERROR_CODE_MAP = {
    400: ClientError,
    401: ClientError,
    403: ClientError,
    404: ClientError,
    429: RateLimitError,
    500: ServerError,
    502: ServerError,
    503: ServerError,
    504: ServerError,
}


def classify_error(status_code: int, response_text: str) -> ModelAPIError:
    """
    根据HTTP状态码分类错误
    
    Args:
        status_code: HTTP状态码
        response_text: 响应文本
        
    Returns:
        对应的异常实例
    """
    error_class = ERROR_CODE_MAP.get(status_code, ModelAPIError)
    return error_class(f"HTTP错误 {status_code}: {response_text}", status_code)


if __name__ == "__main__":
    # 简单测试示例
    @handle_api_errors
    @retry_on_error(max_retries=2)
    @log_api_call
    def test_function(should_fail=False):
        if should_fail:
            raise requests.exceptions.ConnectionError("测试连接错误")
        return {"result": "success"}
    
    try:
        # 测试成功情况
        print("测试成功情况:")
        result = test_function()
        print(f"结果: {result}")
        
        # 测试失败情况
        print("\n测试失败情况:")
        result = test_function(should_fail=True)
        print(f"结果: {result}")
    except Exception as e:
        print(f"捕获到异常: {type(e).__name__}: {str(e)}")